# storage/fsm.py

import struct
from .buffer import BufferManager
from .file_manager import FileManager
from .page import PAGE_SIZE

# 假设FSM页面固定为 page_id=1
FSM_PAGE_ID = 1

# 假设 page 0 (元数据) 和 page 1 (FSM) 存在并初始化。
META_PAGE_ID = 0
META_FORMAT = "!I"  # next_free_page
META_SIZE = struct.calcsize(META_FORMAT)

class FreeSpaceManager:
    """
    管理数据库文件的空闲空间，使用 FSM 页面来跟踪。
    """
    def __init__(self, buffer_manager: BufferManager, file_manager: FileManager):
        self.buffer_manager = buffer_manager
        self.file_manager = file_manager

    def _ensure_system_pages(self):
        """确保 page 0 (元数据) 和 page 1 (FSM) 存在并初始化。"""
        try:
            self.buffer_manager.get_page(FSM_PAGE_ID)
            # 若可获取，说明系统页已存在
            self.buffer_manager.unpin_page(FSM_PAGE_ID)
            return
        except ValueError:
            pass  # 需要创建

        # 创建 meta page 0
        meta_page = self.buffer_manager.new_page()  # page 0
        if meta_page.page_id != META_PAGE_ID:
            raise RuntimeError("Meta page expected id 0")
        # 初始化 next_free_page = 2 (0,1 已占用)
        struct.pack_into(META_FORMAT, meta_page.data, 0, 2)
        self.buffer_manager.unpin_page(meta_page.page_id, is_dirty=True)

        # 创建 FSM page 1
        fsm_page = self.buffer_manager.new_page()  # page 1
        if fsm_page.page_id != FSM_PAGE_ID:
            raise RuntimeError("FSM page expected id 1")
        fsm_page.data[:] = bytes(PAGE_SIZE)
        self.buffer_manager.unpin_page(fsm_page.page_id, is_dirty=True)

    def _get_fsm_page(self):
        """获取并锁定FSM页面，如果不存在则初始化。"""
        self._ensure_system_pages()
        return self.buffer_manager.get_page(FSM_PAGE_ID)

    def find_page_with_space(self, required_space: int) -> int or None:
        """在FSM中查找一个有足够空间的页面。"""
        # 将所需空间转换为0-255的等级
        required_level = (required_space / PAGE_SIZE) * 255

        fsm_page = self._get_fsm_page()
        try:
            fsm_data = fsm_page.get_data()  # 获取页内数据
            # 迭代FSM中的每个字节，每个字节代表一个数据页
            # 跳过 page 0 (元数据) 和 page 1 (FSM本身)
            for page_id, space_level in enumerate(fsm_data[2:], start=2):
                if space_level >= required_level:
                    return page_id
        finally:
            self.buffer_manager.unpin_page(fsm_page.page_id)

        return None

    def update_page_space(self, page_id: int, free_space: int):
        """更新FSM中关于某个数据页的空闲空间信息。"""
        space_level = int((free_space / PAGE_SIZE) * 255)

        fsm_page = self._get_fsm_page()
        try:
            # 直接修改FSM页的数据
            fsm_page.data[page_id] = space_level
            fsm_page.is_dirty = True  # 标记为脏页
        finally:
            # 标记为脏并unpin
            self.buffer_manager.unpin_page(fsm_page.page_id, is_dirty=True)

    def _allocate_sequential_page(self):
        """使用 meta page 的计数器顺序分配新页（已修复）。"""
        meta_page = self.buffer_manager.get_page(META_PAGE_ID)
        try:
            # 我们信任 BufferManager/FileManager 来给我们真实的下一个新页面
            new_page = self.buffer_manager.new_page()

            
            struct.pack_into(META_FORMAT, meta_page.data, 0, new_page.page_id + 1)
            
            meta_page.is_dirty = True
            return new_page
        finally:
            self.buffer_manager.unpin_page(META_PAGE_ID, is_dirty=True)

    def get_new_page(self, required_space: int) -> int:
        """
        获取一个可用页面：先查 FSM，后顺序分配。
        """
        candidate_id = self.find_page_with_space(required_space)
        if candidate_id is not None:
            try:
                page = self.buffer_manager.get_page(candidate_id)
                self.buffer_manager.unpin_page(candidate_id)
                return candidate_id
            except ValueError:
                pass
        new_page = self._allocate_sequential_page()
        self.buffer_manager.unpin_page(new_page.page_id)
        return new_page.page_id